package com.immortal.core.module.secure.advice;

import com.immortal.core.module.secure.anno.Encrypt;
import com.immortal.core.module.secure.anno.Secure;
import com.immortal.core.module.secure.service.EncryptHttpEncoder;
import com.immortal.core.utils.jackson.JacksonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 *
 * <p> 请求响应处理类<br>
 * <p> 对加了@Encrypt的方法的数据进行加密操作
 * @author   wind
 * @version  1.0
 * @since  2018/12/20
 */
@ControllerAdvice
@Slf4j
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {


    private static ThreadLocal<Boolean> encryptLocal = new ThreadLocal<Boolean>();

    public static void setEncryptStatus(boolean status) {
        encryptLocal.set(status);
    }

    @Autowired
    private EncryptHttpEncoder encryptHttpEncoder;


    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.getMethod().getDeclaringClass().getAnnotation(Secure.class) != null
                || returnType.getMethod().getAnnotation(Secure.class) != null
                || returnType.getMethod().getDeclaringClass().getAnnotation(Encrypt.class) != null
                || returnType.getMethod().getAnnotation(Encrypt.class) != null;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 可以通过调用EncryptResponseBodyAdvice.setEncryptStatus(false);来动态设置不加密操作
        Boolean status = encryptLocal.get();
        if (status != null && status == false) {
            encryptLocal.remove();
            return body;
        }

        long startTime = System.currentTimeMillis();
        Encrypt encrypt = null;
        Secure classSecure = returnType.getMethod().getDeclaringClass().getAnnotation(Secure.class);
        Secure secure = returnType.getMethod().getAnnotation(Secure.class);
        Encrypt classEncrypt = returnType.getMethod().getDeclaringClass().getAnnotation(Encrypt.class);
        Encrypt methodEncrypt = returnType.getMethod().getAnnotation(Encrypt.class);
        if (classSecure != null) {
            encrypt = classSecure.encrypt();
        }
        if (secure != null) {
            encrypt = secure.encrypt();
        }

        if (classEncrypt != null) {
            encrypt = classEncrypt;
        }
        if (methodEncrypt != null) {
            encrypt = methodEncrypt;
        }
        if (encrypt != null && !encrypt.ignore()) {
            String content = JacksonUtil.to(body);
            String result = encryptHttpEncoder.encode(content);
            long endTime = System.currentTimeMillis();
            log.debug("Encrypt Time:" + (endTime - startTime));
            return result;
        }

        return body;
    }

}
